// @ts-ignore
import playerWorklet from 'worklet-loader?inline!./player_worklet.js';
import {loadWasmBinary} from "../wasm/opus_helper";
import {buildPlayerInd, buildPlayerReq, PlayEvent, PlayOptCallback, PlayState} from "./player_constant";
import CompoUint8Array from "../utils/weila_compositor_array";
import {base64DecToArr} from "../utils/utils";


class WLPlayer {
    private audioNode?: AudioWorkletNode;
    private audioBufferNode?: AudioBufferSourceNode;
    private audioBuffer?: AudioBuffer;
    private callbackMap: Map<number, PlayOptCallback>;
    private state: PlayState;
    private playEndCallback?: (id: number) => void;

    constructor(private audioContext: any, private sampleRate: number, private id: number) {
        this.callbackMap = new Map<number, PlayOptCallback>();
        this.state = PlayState.PLAYER_CLOSED_STATE;
    }

    public onPlayEndListener(cb: (id: number) => void) {
        this.playEndCallback = cb;
    }

    onMessage(event: MessageEvent) {
        const evt = event.data.evt & 0x00FF;
        const data = event.data.data;
        const callback = this.callbackMap.get(evt);
        let state = -1;

        console.log("WLPlayer onMessage", evt, data, callback)
        switch (evt) {
            case PlayEvent.PLAYER_EVT_OPEN: {
                state = PlayState.PLAYER_OPENED_STATE;
            }
            break;

            case PlayEvent.PLAYER_EVT_RESUME:
            case PlayEvent.PLAYER_EVT_START: {
                state = PlayState.PLAYER_STARTED_STATE;
            }
            break;

            case PlayEvent.PLAYER_EVT_PAUSE: {
                state = PlayState.PLAYER_PAUSED_STATE;
            }
            break;

            case PlayEvent.PLAYER_EVT_STOP: {
                state = PlayState.PLAYER_OPENED_STATE;
            }
            break;

            case PlayEvent.PLAYER_EVT_CLOSE: {
                state = PlayState.PLAYER_CLOSED_STATE;
            }
            break;

            case PlayEvent.PLAYER_EVT_PLAY_END: {
                if (this.playEndCallback) {
                    this.playEndCallback(this.id);
                }
            }
            break;
        }

        if (callback) {
            if (data.errCode == 0) {
                this.state = state;
                callback.resolve(true);
            }else {
                callback.reject(data.errMsg);
            }
        }
    }

    public async open(): Promise<boolean> {
        console.log("player open", this.state);
        if (this.state === PlayState.PLAYER_CLOSED_STATE) {
            let wasmBase64 = localStorage.getItem("WASMB");
            if (!wasmBase64) {
                await loadWasmBinary();
                wasmBase64 = localStorage.getItem("WASMB");
            }
            await this.audioContext.audioWorklet.addModule(playerWorklet);
            this.audioNode = new AudioWorkletNode(this.audioContext, "player_worklet", {
                channelCount: 1,
                processorOptions: {
                    sampleRate: 16000,
                    wasmData: base64DecToArr(wasmBase64),
                    id: this.id
                }
            });

            this.audioNode.port.onmessage = this.onMessage.bind(this);
            const req = buildPlayerReq(PlayEvent.PLAYER_EVT_OPEN, {sampleRate: 16000});
            this.audioNode.port.postMessage(req);
            return new Promise<boolean>((resolve, reject) => {
                const callback = {} as PlayOptCallback;
                callback.resolve = resolve;
                callback.reject = reject;
                this.callbackMap.set(PlayEvent.PLAYER_EVT_OPEN, callback);
            });
        }

        return Promise.reject("状态不对");
    }

    public close(): Promise<boolean> {
        if (this.state !== PlayState.PLAYER_CLOSED_STATE) {
            const req = buildPlayerReq(PlayEvent.PLAYER_EVT_CLOSE);
            this.audioNode!.port.postMessage(req);
            return new Promise<boolean>((resolve, reject) => {
                const callback = {} as PlayOptCallback;
                callback.resolve = resolve;
                callback.reject = reject;
                this.callbackMap.set(PlayEvent.PLAYER_EVT_CLOSE, callback);
            });
        }

        return Promise.reject("状态不对");
    }

    public async start(): Promise<boolean> {
        if (this.state === PlayState.PLAYER_OPENED_STATE) {
            this.audioBuffer = this.audioContext.createBuffer(1, 16000, 16000);
            this.audioBufferNode = this.audioContext.createBufferSource();
            this.audioBufferNode!.buffer = this.audioBuffer!;
            this.audioBufferNode!.loop = true;
            this.audioBufferNode!.connect(this.audioNode!);
            this.audioNode!.connect(this.audioContext.destination);
            this.audioBufferNode!.start();

            const req = buildPlayerReq(PlayEvent.PLAYER_EVT_START);
            this.audioNode!.port.postMessage(req);
            return new Promise<boolean>((resolve, reject) => {
                const callback = {} as PlayOptCallback;
                callback.resolve = resolve;
                callback.reject = reject;
                this.callbackMap.set(PlayEvent.PLAYER_EVT_START, callback);
            })
        }

        return Promise.reject("状态不对");
    }

    public feedData(codedDataList: Uint8Array[]|undefined, isLast: boolean) {
        let ind: any;
        if (isLast) {
            ind = buildPlayerInd(PlayEvent.PLAYER_EVT_FEED, {isLast: true});
            this.audioNode!.port.postMessage(ind);
        }else {
            const compoArray = new CompoUint8Array();
            compoArray.putArrays(codedDataList!);
            const compoBuffer = compoArray.getCompoArray();
            ind = buildPlayerInd(PlayEvent.PLAYER_EVT_FEED, {isLast: false, buffer: compoBuffer, lenList: compoArray.getCompoLengthList()});
            this.audioNode!.port.postMessage(ind, [compoBuffer!.buffer]);
        }

    }

    public async stop(): Promise<boolean> {
        if (this.state === PlayState.PLAYER_STARTED_STATE ||
        this.state === PlayState.PLAYER_PAUSED_STATE) {
            const req = buildPlayerReq(PlayEvent.PLAYER_EVT_STOP);
            this.audioNode!.port.postMessage(req);
            return new Promise<boolean>((resolve, reject) => {
                const callback = {} as PlayOptCallback;
                callback.resolve = resolve;
                callback.reject = reject;
                this.callbackMap.set(PlayEvent.PLAYER_EVT_STOP, callback);
            })
        }

        return Promise.reject("播放器未初始化");
    }

    public async pause(): Promise<boolean> {
        if (this.state === PlayState.PLAYER_STARTED_STATE) {
            const req = buildPlayerReq(PlayEvent.PLAYER_EVT_PAUSE);
            this.audioNode!.port.postMessage(req);
            return new Promise<boolean>((resolve, reject) => {
                const callback = {} as PlayOptCallback;
                callback.resolve = resolve;
                callback.reject = reject;
                this.callbackMap.set(PlayEvent.PLAYER_EVT_PAUSE, callback);
            })
        }

        return Promise.reject("播放器未初始化")
    }

    public async resume(): Promise<boolean> {
        if (this.state === PlayState.PLAYER_PAUSED_STATE) {
            const req = buildPlayerReq(PlayEvent.PLAYER_EVT_RESUME);
            this.audioNode!.port.postMessage(req);
            return new Promise<boolean>((resolve, reject) => {
                const callback = {} as PlayOptCallback;
                callback.resolve = resolve;
                callback.reject = reject;
                this.callbackMap.set(PlayEvent.PLAYER_EVT_RESUME, callback);
            })
        }

        return Promise.reject("播放器未初始化")
    }
}

export {WLPlayer}
